﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.IO;
using System.Diagnostics;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Security;
using Studio.Foundation.Text;

namespace Studio.Foundation.Ontologies
{
    /// <summary>
    /// NOTICE: the class is deprecated. 
    /// You should use XsdCompiler since 2010/06/30
    /// by marstone.
    /// </summary>
    public class XsdConverter
    {
        static string XSD_ARGUMENTS = " -c -l:c# -n:Studio.Xml.Entities \"{0}\"";
        static string FILE_EXT_XSD = ".xsd";
        static string FILE_EXT_CS = ".cs";

        string xsdCommand = "C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0A\\bin\\xsd.exe";
        string xsdUserName = null;
        string xsdPassword = null;

        public XsdConverter(string xsdCommand, string userName, string password)
        {
            if (false == string.IsNullOrEmpty(xsdCommand)) this.xsdCommand = xsdCommand;
            this.xsdUserName = userName;
            this.xsdPassword = password;
        }

        XmlSchemaSet schemaSet = new XmlSchemaSet();

        public XmlSchemaSet SchemaSet
        {
            get { return schemaSet; }
        }

        public Assembly Compile()
        {
            if (false == schemaSet.IsCompiled)
                schemaSet.Compile();

            List<string> files = new List<string>();

            foreach (XmlSchema xsd in schemaSet.Schemas()) 
            {
                string file = this.XsdToFile(xsd);
                if (false == string.IsNullOrEmpty(file))
                    files.Add(file);
            }

            return this.CompileXsdFiles(files);
        }

        public XmlSchema AddSchema(XmlSchema xsd)
        {
            return this.schemaSet.Add(xsd);
        }

        public void ClearSchemas()
        {
            schemaSet = new XmlSchemaSet();
        }

        /// <summary>
        /// Save Xml Schema to a text file and return the full path.
        /// </summary>
        /// <param name="xsd"></param>
        /// <returns></returns>
        public string XsdToFile(XmlSchema xsd)
        {

            Stream stream = null;
            TextWriter writer = null;
            TextReader reader = null;
            string schemaString = string.Empty;
            try
            {
                stream = new MemoryStream();
                writer = new StreamWriter(stream, Encoding.UTF8);
                xsd.Write(stream);
                stream.Position = 0;
                reader = new StreamReader(stream, Encoding.UTF8);
                schemaString = reader.ReadToEnd();
            }
            catch (Exception e)
            {
                System.Diagnostics.Debug.WriteLine(e);
                return null;
            }
            finally
            {
                if (null != writer) writer.Close();
                if (null != reader) reader.Close();
                if (null != stream) stream.Close();
            }

            string xsdfile = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
            xsdfile += FILE_EXT_XSD;
            try
            {
                writer = File.CreateText(xsdfile);
                writer.Write(schemaString);
            }
            catch (Exception e)
            {
                System.Diagnostics.Debug.WriteLine(e);
                return null;
            }
            finally
            {
                if (null != writer) writer.Close();
            }

            return xsdfile;
        }

        /// <summary>
        /// Compile Schemas to Assembly in Memory.
        /// </summary>
        /// <param name="files"></param>
        /// <returns></returns>
        public Assembly CompileXsdFiles(List<string> files)
        {
            List<string> fileNames = new List<string>();
            int i = 0;
            while(i < files.Count)
            { 
                string file = files[i];
                if (Path.GetExtension(file) != XsdConverter.FILE_EXT_XSD)
                {
                    files.RemoveAt(i);
                    continue;
                }
                fileNames.Add(Path.GetFileNameWithoutExtension(file));
                i++;
            }

            string xsdfiles = string.Join("\" \"", files.ToArray());
            string csfile = string.Join("_", fileNames.ToArray()) +XsdConverter.FILE_EXT_CS;

            Stream stream = null;
            TextReader reader = null;

            try
            {
                string arguments = string.Format(XsdConverter.XSD_ARGUMENTS, xsdfiles);
                ProcessStartInfo info = new ProcessStartInfo(this.xsdCommand, arguments);
                info.UseShellExecute = false;
                info.ErrorDialog = false;
                info.CreateNoWindow = true;
                info.RedirectStandardOutput = true; 
                info.WorkingDirectory = Path.GetTempPath();

                if (false == string.IsNullOrEmpty(this.xsdUserName) &&
                            false == string.IsNullOrEmpty(this.xsdPassword))
                {
                    info.UserName = this.xsdUserName;
                    SecureString secure = new SecureString();
                    foreach (char c in this.xsdPassword)
                        secure.AppendChar(c);
                    info.Password = secure;
                }
                Process process = Process.Start(info);
                System.IO.StreamReader oReader2 = process.StandardOutput;
                string sRes = oReader2.ReadToEnd();
                oReader2.Close(); 
                // wait for exit.
                process.WaitForExit(3000);

                if (false == process.HasExited)
                {
                    process.Kill();
                    return null;
                }
                csfile = StringUtilities.Cut(sRes, "Writing file '", "'.");
                // any error?
                if (false == File.Exists(csfile)) return null;

                stream = File.OpenRead(csfile);
                reader = new StreamReader(stream);
                string code = reader.ReadToEnd();

                CodeDomProvider provider = CSharpCodeProvider.CreateProvider("CSharp");
                CompilerParameters parameters = new CompilerParameters();
                // Start by adding any referenced assemblies
                parameters.ReferencedAssemblies.Add("System.dll");
                parameters.ReferencedAssemblies.Add("System.Xml.dll");

                // Load the resulting assembly into memory
                parameters.GenerateInMemory = true;

                // Now compile the whole thing
                CompilerResults compiled = provider.CompileAssemblyFromSource(parameters, code);

                if (compiled.Errors.HasErrors)
                {
                    return null;
                }

                Assembly assembly = compiled.CompiledAssembly;
                return assembly;
            }
            catch (Exception e)
            {
                System.Diagnostics.Debug.WriteLine(e);
                return null;
            }
            finally
            {
                if (null != reader) reader.Close();
                if (null != stream) stream.Close();
            }
        }

        public Assembly XsdToAssembly(XmlSchema xsd)
        {
            string xsdfile = this.XsdToFile(xsd);
            if (false == string.IsNullOrEmpty(xsdfile) || 
                false == xsdfile.EndsWith(XsdConverter.FILE_EXT_XSD))
                return null;

            List<string> files = new List<string>() { xsdfile };

            return this.CompileXsdFiles(files);
        }
    }
}
